#pragma once
/*
 * function traits.  
 */
#include <utility>
#include <type_traits>
#include <tuple>

namespace anet {
	namespace rpc_codec {
		template<typename T>
		struct function_traits_impl;

		template<typename T>
		struct function_traits : function_traits_impl<std::remove_cv_t<std::remove_reference_t<T>>> {
		};

		template<typename Ret, typename ... Args>
		struct function_traits_impl<Ret(Args...)> {
			typedef Ret return_type;

			template<std::size_t I>
			struct args {
				using type = typename std::tuple_element<I, std::tuple<Args...>>::type;
			};

			typedef std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...> tuple_type;
		};

		// partial implementation for function_traits_impl.
		template<typename Ret, typename ... Args>
		struct function_traits_impl<Ret(*)(Args...)> : function_traits_impl<Ret(Args...)> {
		};

		// common class's member function.
		template<typename Ret, typename Cls, typename ... Args>
		struct function_traits_impl<Ret(Cls::*)(Args...)> : function_traits_impl<Ret(Args...)> {
			using class_type = Cls;
		};

		// const for class's member function.
		template<typename Ret, typename Cls, typename ... Args>
		struct function_traits_impl<Ret(Cls::*)(Args...) const> : function_traits_impl<Ret(Args...)> {
			using class_type = Cls;
		};

		// reference.
		template<typename Ret, typename ... Args>
		struct function_traits_impl<Ret(&)(Args...)> : function_traits_impl<Ret(Args...)> {
		};

		// const function.
		template<typename Ret, typename ... Args>
		struct function_traits_impl<Ret(Args...) const> : function_traits_impl<Ret(Args...)> {
		};
	}
}